package minderupt.selenate;

import minderupt.selenate.cli.CommonsCLIGlobalOptions;
import minderupt.selenate.cli.GlobalOptions;
import minderupt.selenate.context.ScriptEnvironment;
import minderupt.selenate.core.SeleniumRunner;
import minderupt.selenate.dependency.DependencyManager;
import minderupt.selenate.reporting.ReportWriter;
import minderupt.selenate.translate.ScriptBuilder;
import minderupt.selenate.translate.Translator;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.mozilla.javascript.Context;
import org.mozilla.javascript.Script;
import org.mozilla.javascript.Scriptable;

import java.io.*;
import java.util.LinkedList;
import java.util.List;

/**
 * Author:  Michael Dowling <michael.dowling@minderupt.com>
 */
public class Selenate {

    private static Log LOGGER = LogFactory.getLog(Selenate.class);
    private GlobalOptions globalOptions;

    public Selenate(GlobalOptions globalOptions) {
        this.globalOptions = globalOptions;
    }


    public void exec() throws Exception {

        if (LOGGER.isDebugEnabled()) LOGGER.debug("Creating Java Selenium Runner.");
        LOGGER.info("Selenium Remote Control:");
        LOGGER.info("\tHost: " + globalOptions.getRcHost());
        LOGGER.info("\tPort: " + globalOptions.getRcPort());
        LOGGER.info("\tStartup: " + globalOptions.getRcStartup());
        LOGGER.info("\tInitial URL: " + globalOptions.getRcInitialUrl());

        SeleniumRunner runner = new SeleniumRunner(globalOptions.getRcHost(),
                globalOptions.getRcPort(),
                globalOptions.getRcStartup(),
                globalOptions.getRcInitialUrl());


        if (LOGGER.isDebugEnabled()) LOGGER.debug("Initializing Javascript Runtime.");
        Context ctx = Context.enter();
        Scriptable scope = ctx.initStandardObjects();

        // inject java selenium runner
        if (LOGGER.isDebugEnabled()) LOGGER.debug("Injecting Java Selenium Runner");
        scope.put("javaSeleniumRunner", scope, runner);

        // parse/eval the JS selenium gateway
        if (LOGGER.isDebugEnabled()) LOGGER.debug("Parsing & Evaling the Javascript Selenium Gateway.");
        InputStream seleniumRunnerInputStream = DependencyManager.getDependencyManager().getResourceAsInputStream("/SeleniumRunner.js");
        String seleniumGatewayRawScript = IOUtils.toString(seleniumRunnerInputStream);
        

        ctx.evaluateString(scope, seleniumGatewayRawScript, "SeleniumRunner.js", 0, null);
        Object jsSelenium = scope.get("jsSeleniumRunner", scope);
        scope.put("selenium", scope, jsSelenium);

        if (LOGGER.isDebugEnabled()) LOGGER.debug("Getting scripts.");
        List<File> scriptFiles = new LinkedList<File>();
        if (globalOptions.getScript() != null) {

            File specScript = new File(globalOptions.getScript());
            scriptFiles.add(specScript);
            LOGGER.info("Script:  " + specScript);

        } else if (globalOptions.getScripts() != null) {

            File baseDir = new File(globalOptions.getScripts());
            if (baseDir.exists() && baseDir.isDirectory()) {

                // get list of .js files
                LOGGER.info("Script directory: " + globalOptions.getScripts());
                String[] files = baseDir.list(new FilenameFilter() {
                    public boolean accept(File dir, String name) {
                        return (name.endsWith(".js"));
                    }
                });

                for (String file : files) {
                    File script = new File(baseDir, file);
                    scriptFiles.add(script);
                    LOGGER.info("\tScript: " + file);
                }


            }


        }


        // open up scripts
        if (LOGGER.isDebugEnabled()) LOGGER.debug("Opening scripts and evaluating.");
        ScriptEnvironment environment = new ScriptEnvironment(ctx, scope);
        scope.put("environment", scope, environment);
        for (File scriptFile : scriptFiles) {

            LOGGER.info("Compiling: " + scriptFile.getName());
            File baseDirectory = scriptFile.getParentFile();
            environment.setCurrentBaseDirectory(baseDirectory);

            Script specScript = ctx.compileReader(new FileReader(scriptFile), scriptFile.getName(), 0, null);
            specScript.exec(ctx, scope);


        }


        LOGGER.info("Executing Selenium.");
        ctx.evaluateString(scope, "selenium.execute()", "JAVA", 0, null);


        ReportWriter reportWriter = (ReportWriter) DependencyManager.getDependencyManager().getDependencyByName("reportWriter");
        reportWriter.writeReport(globalOptions.getReportLocation(), runner.getExecutionResult());


    }



    public static void main(String[] args) throws Exception {


        if (LOGGER.isDebugEnabled()) LOGGER.debug("Bootstrapping the world of Selenate.");
        bootstrap(args);

        GlobalOptions globalOptions = new CommonsCLIGlobalOptions(args);
        if(globalOptions.isImport()) {
            importScript(globalOptions);
            System.exit(0);
        }


        Selenate sel = new Selenate(globalOptions);
        sel.exec();


    }

    private static void importScript(GlobalOptions globalOptions) {

        LOGGER.info("Importing selenese: " + globalOptions.getScript());
        Translator translator = (Translator) DependencyManager.getDependencyManager().getDependencyByName("seleneseTranslator");
        ScriptBuilder builder = (ScriptBuilder) DependencyManager.getDependencyManager().getDependencyByName("javascriptBuilder");

        if(LOGGER.isDebugEnabled()) LOGGER.debug("Opening up selenese file");
        File seleneseFile = new File(globalOptions.getScript());
        if(!seleneseFile.exists()) {
            LOGGER.error("Selenese file does not exist: " + globalOptions.getScript());
            System.exit(1);
        }

        String selenese = null;
        try {
            selenese = FileUtils.readFileToString(seleneseFile);
        } catch(IOException ioe) {
            LOGGER.error("Unable to read selenese file.", ioe);
            System.exit(1);
        }

        String javascript = translator.translate(selenese, builder);

        File output = new File(globalOptions.getScript() + ".js");
        try {
            boolean canCreate = output.createNewFile();
            FileUtils.writeStringToFile(output, javascript);
        } catch(IOException ioe) {
            LOGGER.error("Unable to create javascript file: " + globalOptions.getScript() + ".js", ioe);
            System.exit(1);
        }

        LOGGER.info("Javascript Created: " + globalOptions.getScript() + ".js");
        System.exit(0);


    }

    private static void bootstrap(String[] args) {


    }


}
